﻿using OxyPlot;
using OxyPlot.Annotations;
using OxyPlot.Axes;
using OxyPlot.Wpf;
using PropertyChanged;
using SimpleLogger;
using System;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows.Input;
using WPFTemplate;
using WPFTemplateLib.UserControls;
using WPFTemplateLib.WpfHelpers;
using ConsoleWriter = DotNet.Utilities.ConsoleHelper.ConsoleWriter;
using HorizontalAlignment = OxyPlot.HorizontalAlignment;
using TickStyle = OxyPlot.Axes.TickStyle;
using Timer = System.Timers.Timer;

namespace OxyPlotTester.ViewModels
{
    [AddINotifyPropertyChangedInterface]
    public class MainWindowViewModel : BindableBase
    {
        #region Bindable

        private ConfigItems _configs;
        public ConfigItems Configs
        {
            get
            {
                if (_configs == null)
                {
                    LoadConfigCommand.Execute(false);
                }

                return _configs;
            }

            set => _configs = value;
        }

        private string _Info = "";
        /// <summary>
        /// 信息窗内容;
        /// </summary>
        public string Info
        {
            get => _Info;

            set
            {
                //保证 选择不自动滚动消息 的效果（通过不通知界面实现）,不这样的话新消息来的时候，光标/滚动条 会跳到最上面.

                if (Configs.IsAutoScroll)
                {
                    SetProperty(ref _Info, value);
                }
                else
                {
                    _Info = value;
                }
            }
        }

        /// <summary>
        /// 底部状态栏信息
        /// </summary>
        public string Status { get; set; } = "";

        /// <summary>
        /// 弹窗VM;
        /// </summary>
        public ConfirmBoxViewModel DialogVm { get; set; } = new ConfirmBoxViewModel();

        /// <summary>
        /// 时间间隔显示值（毫秒）
        /// </summary>
        public string IntervalStr { get; set; } = "1000";

        /// <summary>
        /// 时间间隔（毫秒）
        /// </summary>
        public int Interval { get; set; } = 1000;

        /// <summary>
        /// 注解横坐标
        /// </summary>
        public double AnnotationX { get; set; } = 4;

        /// <summary>
        /// 注解纵坐标
        /// </summary>
        public double AnnotationY { get; set; } = 4;

        private bool _isExporting;
        /// <summary>
        /// 是否正在导出
        /// </summary>
        public bool IsExporting
        {
            get => _isExporting;
            set => SetPropertyWithoutCompare(ref _isExporting, value);
        }

        /// <summary>
        /// 导出文件夹路径
        /// </summary>
        public string ExportPath { get; set; }

        #endregion

        public MainWindowViewModel()
        {
            Console.SetOut(new ConsoleWriter(ShowInfo));

            SetCommandMethod();

            SetOxyStyle();
            _simulatePcrTimer.Elapsed += SimulatePcrTimerOnTick;
        }

        #region Command

        /// <summary>
        /// 保存配置命令
        /// </summary>
        public ICommand SaveConfigCommand { get; set; }

        /// <summary>
        /// 载入配置命令
        /// </summary>
        public ICommand LoadConfigCommand { get; set; }

        /// <summary>
        /// 清空消息命令
        /// </summary>
        public ICommand ClearInfoCommand { get; set; }

        /// <summary>
        /// 关于弹窗命令
        /// </summary>
        public ICommand AboutCommand { get; set; }

        /// <summary>
        /// 开始模拟命令
        /// </summary>
        public ICommand StartSimulateCmd { get; set; }

        /// <summary>
        /// 停止模拟命令
        /// </summary>
        public ICommand StopSimulateCmd { get; set; }

        /// <summary>
        /// 设置时间间隔命令
        /// </summary>
        public ICommand SetIntervalCmd { get; set; }

        /// <summary>
        /// 显示十字注解命令
        /// </summary>
        public ICommand ShowCrossAnnotationCmd { get; set; }

        /// <summary>
        /// 隐藏十字注解命令
        /// </summary>
        public ICommand HideCrossAnnotationCmd { get; set; }

        /// <summary>
        /// 导出Png图片命令
        /// </summary>
        public ICommand ExportPngCmd { get; set; }

        /// <summary>
        /// 导出图片（附加属性方式）命令
        /// </summary>
        public ICommand ExportPicAttachedCmd { get; set; }

        #endregion

        /// <summary>
        /// 命令方法赋值(在构造函数中调用)
        /// </summary>
        private void SetCommandMethod()
        {
            SaveConfigCommand ??= new RelayCommand(o => true, async o =>
            {
                if (ConfigManager.SaveConfig(Configs))
                {
                    await ConfirmBoxHelper.ShowMessage(DialogVm, "保存配置成功");
                }
                else
                {
                    await ConfirmBoxHelper.ShowMessage(DialogVm, "保存配置失败");
                }
            });

            LoadConfigCommand ??= new RelayCommand(o => true, async o =>
            {
                if (ConfigManager.LoadConfig<ConfigItems>(ref _configs))
                {
                    if (o as bool? != false)
                    {
                        await ConfirmBoxHelper.ShowMessage(DialogVm, "载入配置成功");
                    }
                }
                else
                {
                    await ConfirmBoxHelper.ShowMessage(DialogVm, "载入配置失败");
                }
            });

            ClearInfoCommand ??= new RelayCommand(o => true, o =>
            {
                Info = "";
            });

            AboutCommand ??= new RelayCommand(o => true, o =>
            {
                new AboutWindow().ShowDialog();
            });

            StartSimulateCmd ??= new RelayCommand(o => true, async o =>
            {
                _simulatePcrTimer.Start();
            });

            StopSimulateCmd ??= new RelayCommand(o => true, o =>
            {
                _simulatePcrTimer.Stop();
            });

            SetIntervalCmd ??= new RelayCommand(o => true, async o =>
            {
                if(int.TryParse(IntervalStr, out int value) && value > 0)
                {
                    Interval = value;
                }
                else
                {
                    IntervalStr = "1000";
                    Interval = 1000;
                    await ConfirmBoxHelper.ShowMessage(DialogVm, "请输入正确的时间间隔（毫秒）", 3);
                }
            });

            ShowCrossAnnotationCmd ??= new RelayCommand(o => true, async o =>
            {
                _lineAnnotationH.Y = AnnotationY;
                _lineAnnotationV.X = AnnotationX;
                _imageAnnotationC.X = new PlotLength(AnnotationX, PlotLengthUnit.Data);
                _imageAnnotationC.Y = new PlotLength(AnnotationY, PlotLengthUnit.Data);

                if (!PlotModel.Annotations.Contains(_lineAnnotationH))
                {
                    PlotModel.Annotations.Add(_lineAnnotationH);
                    PlotModel.Annotations.Add(_lineAnnotationV);
                    PlotModel.Annotations.Add(_imageAnnotationC);
                }

                PlotModel.InvalidatePlot(false);
            });

            HideCrossAnnotationCmd ??= new RelayCommand(o => true, async o =>
            {
                if (PlotModel.Annotations.Contains(_lineAnnotationH))
                {
                    PlotModel.Annotations.Remove(_lineAnnotationH);
                    PlotModel.Annotations.Remove(_lineAnnotationV);
                    PlotModel.Annotations.Remove(_imageAnnotationC);

                    PlotModel.InvalidatePlot(false);
                }
            });

            ExportPngCmd ??= new RelayCommand(o => true, async o =>
            {
                var pngExporter = new PngExporter { Width = (int)PlotModel.Width, Height = (int)PlotModel.Height, };
                //string exportPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Export");
                string exportPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "Export");
                if (!Directory.Exists(exportPath))
                {
                    Directory.CreateDirectory(exportPath);
                }

                pngExporter.ExportToFile(PlotModel, Path.Combine(exportPath, $"{DateTime.Now:yyyyMMdd_HHmmss}.png"));
                await ConfirmBoxHelper.ShowMessage(DialogVm, "导出完成", 3);
            });

            ExportPicAttachedCmd ??= new RelayCommand(o => true, async o =>
            {
                //此处如果不设置，则第二次导出触发不了;
                IsExporting = false;

                await ConfirmBoxHelper.ShowWait(DialogVm, "正在导出", async () =>
                {
                    ExportPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory),
                        "MyExport");
                    IsExporting = true;
                });
            });
        }

        #region 辅助方法

        private void ShowInfo(string info, [CallerFilePath] string filePath="", [CallerMemberName] string memberName="", [CallerLineNumber] int lineNumber=0)
        {
            if (Info.Length > Configs.AutoHalveThresholdValue && Configs.IsAutoHalve)
            {
                Info = "(已删除一半信息)\r\n" + Info.Remove(0, Configs.AutoHalveThresholdValue / 2);
            }

            Info += $"[{DateTime.Now:HH:mm:ss.ffff}] {info}\r\n\r\n";

            if (Configs.IsRecordToLog)
            {
                LogHelper.Write(info, true, filePath, memberName, lineNumber);
            }
        }

        #endregion

        #region OxyPlot

        /// <summary>
        /// 温度曲线数据源（OxyPlot）
        /// </summary>
        public PlotModel PlotModel { get; set; } = new PlotModel();

        /// <summary>
        /// 线条
        /// </summary>
        private OxyPlot.Series.LineSeries _Series = new OxyPlot.Series.LineSeries()
        {
            /*InterpolationAlgorithm = InterpolationAlgorithms.CanonicalSpline*/
        };

        //坐标轴
        private LinearAxis _AxisLeft = new LinearAxis(){ Position = AxisPosition.Left };
        private LinearAxis _AxisBottom = new LinearAxis(){ Position = AxisPosition.Bottom };

        //注解
        private LineAnnotation _lineAnnotationH = new LineAnnotation { Type = LineAnnotationType.Horizontal };
        private LineAnnotation _lineAnnotationV = new LineAnnotation { Type = LineAnnotationType.Vertical };
        private ImageAnnotation _imageAnnotationC = new ImageAnnotation();

        /// <summary>
        /// 注解线条点击时的缩放比例
        /// </summary>
        private int _annotationScale = 2;

        /// <summary>
        /// 图片注解（十字靶心）尺寸（宽和高）
        /// </summary>
        private double _imageAnnotationSize = 66;

        private Random _random;
        private double totalSecond = 0;

        /// <summary>
        /// 模拟扩增定时器
        /// </summary>
        private Timer _simulatePcrTimer = new Timer() { Interval = 100 };

        /// <summary>
        /// 模拟扩增定时器执行方法
        /// </summary>
        private void SimulatePcrTimerOnTick(object sender, EventArgs e)
        {
            try
            {
                if (_simulatePcrTimer.Interval == 100)
                {
                    _random = new Random();
                }

                totalSecond += _simulatePcrTimer.Interval;
                _simulatePcrTimer.Interval = Interval;

                _Series.Points.Add(new DataPoint(totalSecond / 1000 / 60.0, (double)_random.Next(5, 100)));

                //调用该方法可避免点击鼠标右键后图形不自动向左挤压（禁用鼠标右键触发 Pan 命令则可不调用这个方法）;
                //PlotModel.ResetAllAxes();
                PlotModel.InvalidatePlot(true); //刷新数据;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                Task.Run(async () =>
                {
                    await ConfirmBoxHelper.ShowMessage(DialogVm, $"发生异常：{ex.Message}", 6);
                });
            }
        }

        /// <summary>
        /// 设置 OxyPlot 样式
        /// </summary>
        private void SetOxyStyle()
        {
            //图表边框;
            PlotModel.PlotAreaBorderThickness = new OxyThickness(1,0,0,1);
            //PlotModel.PlotAreaBorderThickness = new OxyThickness(0);
            PlotModel.PlotAreaBorderColor = OxyColor.Parse("#9C9C9C");
            //PlotModel.PlotAreaBackground = OxyColor.Parse("#FFFFFF");
            PlotModel.Background = OxyColor.Parse("#FFFFFF");
            PlotModel.TextColor = OxyColor.Parse("#1E436B");
            PlotModel.DefaultFontSize = 14;

            //线条;
            _Series.Color = OxyColor.Parse("#32C5FF");
            _Series.StrokeThickness = 4;
            _Series.TrackerFormatString = "时间: {2:0.##}\n温度: {4:0.##}";
            //是否在没有实际点的地方显示（插值）Tracker（点信息）;
            _Series.CanTrackerInterpolatePoints = false;

            #region 坐标轴

            //数值范围;
            _AxisLeft.AbsoluteMinimum = 0;
            _AxisLeft.AbsoluteMaximum = 100;
            //坐标轴固定范围;
            _AxisLeft.Minimum = 0;
            _AxisLeft.Maximum = 100;
            //坐标轴线设置（需要把图表边框设为0才能生效）
            _AxisLeft.AxislineStyle = LineStyle.Solid;
            _AxisLeft.AxislineColor = OxyColor.Parse("#9C9C9C");
            _AxisLeft.AxislineThickness = 1;
            //缩放;
            _AxisLeft.IsZoomEnabled = false;
            //平移（按住鼠标右键平移）
            _AxisLeft.IsPanEnabled = false;
            //刻度
            _AxisLeft.TickStyle = TickStyle.None;
            //小刻度线;
            _AxisLeft.MinorTickSize = 0;
            //主网格线;
            _AxisLeft.MajorGridlineStyle = LineStyle.Solid;
            _AxisLeft.MajorGridlineColor = OxyColor.Parse("#F3F3F3");

            //坐标轴线设置（需要把图表边框设为0才能生效）
            _AxisBottom.AxislineStyle = LineStyle.Solid;
            _AxisBottom.AxislineColor = OxyColor.Parse("#9C9C9C");
            _AxisBottom.AxislineThickness = 1;
            //缩放;
            _AxisBottom.IsZoomEnabled = false;
            //平移（按住鼠标右键平移）
            _AxisBottom.IsPanEnabled = false;
            //刻度
            _AxisBottom.TickStyle = TickStyle.None;
            //小刻度线;
            _AxisBottom.MinorTickSize = 0;
            //主网格线;
            _AxisBottom.MajorGridlineStyle = LineStyle.Solid;
            _AxisBottom.MajorGridlineColor = OxyColor.Parse("#F3F3F3");

            #endregion

            //PlotModel.Axes.Clear();
            PlotModel.Axes.Add(_AxisLeft);
            PlotModel.Axes.Add(_AxisBottom);
            PlotModel.Series.Add(_Series);

            SetCrossAnnotation();
        }

        /// <summary>
        /// 设置十字注解
        /// </summary>
        private void SetCrossAnnotation()
        {
            _lineAnnotationH.Color = OxyColor.Parse("#FF1700FF");
            _lineAnnotationV.Color = OxyColor.Parse("#FF1700FF");
            _lineAnnotationH.LineStyle = LineStyle.Solid;
            _lineAnnotationV.LineStyle = LineStyle.Solid;
            _lineAnnotationH.StrokeThickness = 5;
            _lineAnnotationV.StrokeThickness = 5;

            _lineAnnotationH.MouseDown += LineAnnotationOnMouseDown;
            _lineAnnotationV.MouseDown += LineAnnotationOnMouseDown;

            _lineAnnotationH.MouseUp += LineAnnotationOnMouseUp;
            _lineAnnotationV.MouseUp += LineAnnotationOnMouseUp;

            // Handle mouse movements (note: this is only called when the mousedown event was handled)
            _lineAnnotationH.MouseMove += LineAnnotationHOnMouseMove;
            _lineAnnotationV.MouseMove += LineAnnotationVOnMouseMove;

            //十字靶心图片注解;
            //_imageAnnotationC.ImageSource = new OxyImage(typeof(MainWindowViewModel).GetTypeInfo().Assembly.GetManifestResourceStream("OxyPlotTester.Resources.cross.png"));
            _imageAnnotationC.ImageSource = new OxyImage(Assembly.GetExecutingAssembly().GetManifestResourceStream($"{typeof(App).Namespace}.Resources.cross.png"));
            _imageAnnotationC.Width = new PlotLength(_imageAnnotationSize, PlotLengthUnit.ScreenUnits);
            _imageAnnotationC.Height = new PlotLength(_imageAnnotationSize, PlotLengthUnit.ScreenUnits);
            _imageAnnotationC.VerticalAlignment = VerticalAlignment.Middle;
            _imageAnnotationC.HorizontalAlignment = HorizontalAlignment.Center;

            _imageAnnotationC.MouseDown += ImageAnnotationOnMouseDown;
            _imageAnnotationC.MouseUp += ImageAnnotationOnMouseUp;
            _imageAnnotationC.MouseMove += ImageAnnotationCOnMouseMove;
        }

        /// <summary>
        /// 直线注解鼠标按下事件方法
        /// </summary>
        private void LineAnnotationOnMouseDown(object? sender, OxyMouseDownEventArgs e)
        {
            if (e.ChangedButton != OxyMouseButton.Left)
            {
                return;
            }

            (sender as LineAnnotation).StrokeThickness *= _annotationScale;
            PlotModel.InvalidatePlot(false);
            e.Handled = true;
        }

        /// <summary>
        /// 直线注解鼠标抬起事件方法
        /// </summary>
        private void LineAnnotationOnMouseUp(object? sender, OxyMouseEventArgs e)
        {
            (sender as LineAnnotation).StrokeThickness /= _annotationScale;
            PlotModel.InvalidatePlot(false);
            e.Handled = true;
        }

        /// <summary>
        /// 横线注解鼠标移动事件方法
        /// </summary>
        private void LineAnnotationHOnMouseMove(object? sender, OxyMouseEventArgs e)
        {
            var la = (LineAnnotation) sender;
            la.Y = la.InverseTransform(e.Position).Y;
            AnnotationY = la.Y;
            PlotModel.InvalidatePlot(false);
            e.Handled = true;
        }

        /// <summary>
        /// 竖线注解鼠标移动事件方法
        /// </summary>
        private void LineAnnotationVOnMouseMove(object? sender, OxyMouseEventArgs e)
        {
            var la = (LineAnnotation)sender;
            la.X = la.InverseTransform(e.Position).X;
            AnnotationX = la.X;
            PlotModel.InvalidatePlot(false);
            e.Handled = true;
        }

        /// <summary>
        /// 图片注解鼠标按下事件方法
        /// </summary>
        private void ImageAnnotationOnMouseDown(object? sender, OxyMouseDownEventArgs e)
        {
            if (e.ChangedButton != OxyMouseButton.Left)
            {
                return;
            }

            var newSize = _imageAnnotationSize *= _annotationScale;
            (sender as ImageAnnotation).Width = new PlotLength(newSize, PlotLengthUnit.ScreenUnits);
            (sender as ImageAnnotation).Height = new PlotLength(newSize, PlotLengthUnit.ScreenUnits);
            PlotModel.InvalidatePlot(false);
            e.Handled = true;
        }

        /// <summary>
        /// 图片注解鼠标抬起事件方法
        /// </summary>
        private void ImageAnnotationOnMouseUp(object? sender, OxyMouseEventArgs e)
        {
            var newSize = _imageAnnotationSize /= _annotationScale;
            (sender as ImageAnnotation).Width = new PlotLength(newSize, PlotLengthUnit.ScreenUnits);
            (sender as ImageAnnotation).Height = new PlotLength(newSize, PlotLengthUnit.ScreenUnits);
            PlotModel.InvalidatePlot(false);
            e.Handled = true;
        }

        /// <summary>
        /// 十字靶心图片注解鼠标移动事件方法
        /// </summary>
        private void ImageAnnotationCOnMouseMove(object? sender, OxyMouseEventArgs e)
        {
            e.Handled = true;
            AnnotationX = _imageAnnotationC.InverseTransform(e.Position).X;
            AnnotationY = _imageAnnotationC.InverseTransform(e.Position).Y;
            _imageAnnotationC.X = new PlotLength(AnnotationX, PlotLengthUnit.Data);
            _imageAnnotationC.Y = new PlotLength(AnnotationY, PlotLengthUnit.Data);
            _lineAnnotationH.Y = AnnotationY;
            _lineAnnotationV.X = AnnotationX;

            //PlotModel.InvalidatePlot(false);
            PlotModel.UpdateAnnotations();
        }

        #endregion
    }
}
